"-----------------------------------------------------------------------------
" Vim Plugin for Verilog Code Automactic Generation 
"------------------------------------------------------------------------------
if exists("b:current_syntax")
	if b:current_syntax != "verilog" && b:current_syntax != "verilog_systemverilog" 
		finish
	endif
endif
if exists("g:loaded_automatic_verilog_snippet") || &cp
	finish
endif
let g:loaded_automatic_verilog_snippet = 1

" Snowind 01/29/2022: "Record update 
" Snowind 01/29/2022: autocmd BufWrite snippet.vim call s:UpdateVimscriptLastModifyTime()
" Snowind 01/29/2022: function s:UpdateVimscriptLastModifyTime()
" Snowind 01/29/2022:     let line = getline(5)
" Snowind 01/29/2022:     if line =~ '\" Last Modified'
" Snowind 01/29/2022:         call setline(5,"\" Last Modified:  " . strftime("%Y/%m/%d %H:%M"))
" Snowind 01/29/2022:     endif
" Snowind 01/29/2022: endfunction

"Defaults settings {{{1

" Function: s:InitVariable() function {{{2
" This function is used to initialise a given variable to a given value. The
" variable is only initialised if it does not exist prior
"
" Args:
"   -var: the name of the var to be initialised
"   -value: the value to initialise var to
"
" Returns:
"   0
function s:InitVariable(var, value)
    if !exists(a:var)
        execute 'let ' . a:var . ' = ' . string(a:value)
    endif
endfunction

" Function: s:GetGlobalSetting : Get a setting from a global variable.   {{{2
"
" Parameters:
"   varname - name of the variable (string)
"   glbname - name of the global variable (string, optional)
" Returns:
"   -
"
" If 'glbname' is given, it is used as the name of the global variable.
" Otherwise the global variable will also be named 'varname'.
"
" If g:<glbname> exists, assign:
"   s:<varname> = g:<glbname>
"-------------------------------------------------------------------------------
function! s:GetGlobalSetting ( varname, ... )
	let lname = a:varname
	let gname = a:0 >= 1 ? a:1 : lname
	if exists( 'g:'.gname )
		let {'s:'.lname} = {'g:'.gname}
	endif
endfunction
" 2}}}

" Snowind 01/29/2022: let g:_ATV_SNIPPET_DEFAULTS = {
            " Snowind 01/29/2022: \'author':      s:AUTHOR,
            " Snowind 01/29/2022: \'company':     S:COMPANY,
            " Snowind 01/29/2022: \'project':     s:PROJECT,
            " Snowind 01/29/2022: \'device':      'Xilinx',
            " Snowind 01/29/2022: \'email':       s:EMAIL,
            " Snowind 01/29/2022: \'website':     '',
            " Snowind 01/29/2022: \'st_pos':      4,
            " Snowind 01/29/2022: \'clk':         'clk',
            " Snowind 01/29/2022: \'rst':         'rst',
            " Snowind 01/29/2022: \'rst_n':       'rst_n',
            " Snowind 01/29/2022: \'att_en':      1
            " Snowind 01/29/2022: \}

" Snowind 01/29/2022: for s:key in keys(g:_ATV_SNIPPET_DEFAULTS)
" Snowind 01/29/2022:     if !exists('g:atv_snippet_' . s:key)
" Snowind 01/29/2022:         let g:atv_snippet_{s:key} = copy(g:_ATV_SNIPPET_DEFAULTS[s:key])
" Snowind 01/29/2022:     endif
" Snowind 01/29/2022: endfor
" Snowind 01/29/2022: let s:indent1 = repeat(' ',g:atv_snippet_indent)
let s:AUTHOR  = ''
let s:EMAIL		= ''
let s:COMPANY = ''
let s:PROJECT = ''

" ident number
let s:atv_snippet_indent = 4 
let s:atv_snippet_clk    = 'clk'
let s:atv_snippet_rst    = 'rst'
"let s:atv_snippet_rst_n  = 'rst_n'


let s:indent1 = repeat(' ',s:atv_snippet_indent)
let s:indent2 = s:indent1.s:indent1
let s:indent3 = s:indent1.s:indent2

call s:GetGlobalSetting('AUTHOR')
call s:GetGlobalSetting('EMAIL')
call s:GetGlobalSetting('COMPANY')
call s:GetGlobalSetting('PROJECT')

" Snowind 01/29/2022: added
let s:cmtStr = '//'
" header line num for AutoTemplate"
let s:append_line_num = 0


"}}}1

"Menu&Mapping {{{1
amenu &Verilog.Code.Always@.always\ @(posedge\ or\ posedge)<TAB><<Leader>al>    :call <SID>AlBpp()<CR>
amenu &Verilog.Code.Always@.always\ @(posedge\ or\ negedge)                     :call <SID>AlBpn()<CR>
amenu &Verilog.Code.Always@.always\ @(*)                                        :call <SID>AlB()<CR>
amenu &Verilog.Code.Always@.always\ @(negedge\ or\ negedge)                     :call <SID>AlBnn()<CR>
amenu &Verilog.Code.Always@.always\ @(posedge)                                  :call <SID>AlBp()<CR>
amenu &Verilog.Code.Always@.always\ @(negedge)                                  :call <SID>AlBn()<CR>
amenu &Verilog.Code.Header.AddHeader<TAB><<Leader>hd>                           :call <SID>AddHeader()<CR>
amenu &Verilog.Code.Comment.SingleLineComment<TAB><<Leader>//>                  :call <SID>AutoComment()<CR>
amenu &Verilog.Code.Comment.MultiLineComment<TAB>Visual-Mode\ <<Leader>/*>      <Esc>:call <SID>AutoComment2()<CR>
amenu &Verilog.Code.Comment.CurLineAddComment<TAB><Leader>/$>                   :call <SID>AddCurLineComment()<CR>
amenu &Verilog.Code.Template.LoadTemplate<TAB>                                  :AtvLoadTemplate<CR>
if !hasmapto('<Leader>hd')
    nnoremap <Leader>hd                                                 :call <SID>AddHeader()<CR>
endif
if !hasmapto('<Leader>al')
    nnoremap <Leader>al                                                 :call <SID>AlBpp()<CR>
endif
if !hasmapto('<Leader>//','n')
    nnoremap <Leader>//                                                 :call <SID>AutoComment()<CR>
endif
if !hasmapto('<Leader>//','v')
    vnoremap <Leader>//                                                 <Esc>:call <SID>AutoComment2()<CR>
endif
if !hasmapto('<Leader>/e')
    nnoremap <Leader>/e                                                 :call <SID>AddCurLineComment()<CR>
endif
noremap <script> <Plug>Atv_Snippet_AddHeader;                           :call <SID>AddHeader()<CR>
noremap <script> <Plug>Atv_Snippet_AlBpp;                               :call <SID>AlBpp()<CR>
noremap <script> <Plug>Atv_Snippet_AlBpn;                               :call <SID>AlBpn()<CR>
noremap <script> <Plug>Atv_Snippet_AlB;                                 :call <SID>AlB()<CR>
noremap <script> <Plug>Atv_Snippet_AlBnn;                               :call <SID>AlBnn()<CR>
noremap <script> <Plug>Atv_Snippet_AlBp;                                :call <SID>AlBp()<CR>
noremap <script> <Plug>Atv_Snippet_AlBn;                                :call <SID>AlBn()<CR>
noremap <script> <Plug>Atv_Snippet_AutoComment;                         :call <SID>AutoComment()<CR>
noremap <script> <Plug>Atv_Snippet_AutoComment2;                        <Esc>:call <SID>AutoComment2()<CR>
noremap <script> <Plug>Atv_Snippet_AddCurLineComment;                   :call <SID>AddCurLineComment()<CR>
"}}}1

"Header

" # Snowind, 01/29/2022 ... added
" insert line_str to line_no and do auto line num incr"
function! s:appendLine(line_str)
	call append(s:append_line_num, a:line_str)
	let s:append_line_num = s:append_line_num + 1
endfunction

function s:AddHeader() "{{{1
	let s:append_line_num = 0
	let headcmtline = '////////////////////////////////////////////////////////////////////////'

	let line = getline(1)
	" "Do not add header if existed
	" Snowind 01/29/2022: if line =~ '// +FHDR'               "Do not add header if existed
	if line =~ headcmtline 
		return
	endif

	let filename = expand("%")
	let modulename = matchstr(filename,'\w\+')
	call s:appendLine(s:cmtStr . "////////////////////////////////////////////////////////////////////////")
	call s:appendLine(s:cmtStr . " Copyright (c) ".strftime("%Y ").s:VERILOG_Company.". All rights reserved.")
	call s:appendLine(s:cmtStr . " ")
	call s:appendLine(s:cmtStr . " This file and associated deliverables are the trade secrets,")
	call s:appendLine(s:cmtStr . " confidential information and copyrighted works of ".s:VERILOG_Company." Corporation,")
	call s:appendLine(s:cmtStr . " and are subject to your license agreement with ".s:VERILOG_Company.".")
	call s:appendLine(s:cmtStr . "")
	call s:appendLine(s:cmtStr . " Use of these deliverables for the purpose of making silicon from an IC")
	call s:appendLine(s:cmtStr . " design is limited to the terms and conditions of your license agreement")
	call s:appendLine(s:cmtStr . " with ".s:VERILOG_Company." If you have further questions please contact ".s:VERILOG_Company.".")
	call s:appendLine(s:cmtStr . "------------------------------------------------------------------------")
	call s:appendLine(s:cmtStr . " File       :  ".filename)
	call s:appendLine(s:cmtStr . " Project    :  ".s:PROJECT)
	call s:appendLine(s:cmtStr . " Module		 :  ".modulename)
	call s:appendLine(s:cmtStr . " Author     :  ".s:AUTHOR)
	call s:appendLine(s:cmtStr . " Create On  :  ".strftime("%m/%d/%Y %H:%M"))
	call s:appendLine(s:cmtStr . " Description:")
	call s:appendLine(s:cmtStr . "")
	call s:appendLine(s:cmtStr . " Revision	 :  $Revision:$")
	call s:appendLine(s:cmtStr . " Checked in :  $DateTime:$")
	call s:appendLine(s:cmtStr . " Modified by:  $Author:$")
	call s:appendLine(s:cmtStr . " Change     :  $Change:$")
	call s:appendLine(s:cmtStr . "")
	call s:appendLine(s:cmtStr . " $Id:$")
	call s:appendLine(s:cmtStr . "////////////////////////////////////////////////////////////////////////")

endfunction 
"}}}1

" Snowind 01/29/2022: "Update Last Modify Time{{{1
" Snowind 01/29/2022: augroup filetype_verilog
" Snowind 01/29/2022:     "incase of no detection of systemverilog, don't use FileType
" Snowind 01/29/2022:     autocmd BufWrite *.v call s:UpdateLastModifyTime()
" Snowind 01/29/2022:     autocmd BufWrite *.sv call s:UpdateLastModifyTime()
" Snowind 01/29/2022: augroup END
" Snowind 01/29/2022: function s:UpdateLastModifyTime() "{{{2
" Snowind 01/29/2022:     let idx = 0
" Snowind 01/29/2022:     for line in getline(1,10)
" Snowind 01/29/2022:         let idx = idx + 1
" Snowind 01/29/2022:         if line =~ '// Last Modified'
" Snowind 01/29/2022:             call setline(idx,"// Last Modified : " . strftime("%Y/%m/%d %H:%M"))
" Snowind 01/29/2022:             return
" Snowind 01/29/2022:         endif
" Snowind 01/29/2022:     endfor
" Snowind 01/29/2022: endfunction "}}}2
" Snowind 01/29/2022: "}}}1

"AutoTemplate{{{1
" Snowind 01/29/2022: augroup filetype_verilog
" Snowind 01/29/2022:     autocmd BufNewFile *.v call s:AutoTemplate()
" Snowind 01/29/2022: augroup END
" Snowind 01/29/2022:
function s:AutoTemplate() "{{{2
	" Snowind 01/29/2022: if g:atv_snippet_att_en == 0
	" Snowind 01/29/2022:   return
	" Snowind 01/29/2022: endif
	let filename = expand("%")
	let modulename = matchstr(filename,'\w\+')
	call s:AddHeader()
	if modulename == 'tb_top'
		call s:appendLine("")
		call s:appendLine("reg clk;")
		call s:appendLine("reg rst_n;")
		call s:appendLine("")
		call s:appendLine("/*autodef off*/")
		call s:appendLine("initial begin")
		call s:appendLine(s:indent1."clk = 1'b0;")
		call s:appendLine(s:indent1."forever #10 clk = ~clk;")
		call s:appendLine("end")
		call s:appendLine("")
		call s:appendLine("initial begin")
		call s:appendLine(s:indent1."rst_n = 1'b0;")
		call s:appendLine(s:indent1."#52 rst_n = 1'b1;")
		call s:appendLine("end")
		call s:appendLine("")
		call s:appendLine("initial begin")
		call s:appendLine(s:indent1."$vcdpluson(0,top);")
		call s:appendLine("end")
		call s:appendLine("")
		call s:appendLine("initial begin")
		call s:appendLine(s:indent1."#1000;")
		call s:appendLine(s:indent1."$finish;")
		call s:appendLine("end")
		call s:appendLine("")
		call s:appendLine("/*autodef on*/")
		call s:appendLine("")
		call s:appendLine('/*autodef*/')
		call s:appendLine("")
		call s:appendLine("//inst u_inst(")
		call s:appendLine("//	/*autoinst*/)
		call s:appendLine("//);")
		call s:appendLine("")
		call s:appendLine("")
		call s:appendLine("endmodule")
		call search('inst')
	else
		call s:appendLine("")
		call s:appendLine("input    clk;")
		call s:appendLine("input    rst_n;")
		call s:appendLine("output   data;")
		call s:appendLine("inout		data_oen;")
		call s:appendLine("")
		call s:appendLine('/*autodef*/')
		call s:appendLine("")
		call s:appendLine("endmodule")
		call search('clk')
	endif
endfunction "}}}2
"}}}1

"Always Block

"===  FUNCTION  ================================================================
"          NAME:  AddAlwaysBlk{{{1
"   DESCRIPTION:  add always block
"    PARAMETERS:  mode - clk Vs rst pos/negedge modes
"===============================================================================
function s:AddAlwaysBlk(mode, clk_name, rst_name) "{{{1
    "let lnum = line(".")
		
		let clk_name = a:clk_name
		let rst_name = a:rst_name

		let comb_logic = 0;
		let clk_active_edge 	 = 'posedge'
		let clk_active_value   = '1'
		let rst_active_edge 	 = 'posedge'
		let rst_active_value   = '1'

		" always sensitive list string
		let sens_list = ''
		if a:mode == 'comb'
			let comb_logic = 1
			let rst_name = ''
			let sens_list = '.*'
		elseif a:mode == 'p'
			let rst_name = ''
			let sens_list = clk_active_edge.' '.clk_name
		elseif a:mode == 'n'
			let clk_active_edge 	 = 'negedge'

			let rst_name = ''
			let sens_list = clk_active_edge.' '.clk_name
		elseif a:mode == 'pn'
			let rst_active_edge 	 = 'negedge'
			let rst_active_value   = '0'

			let rst_name 					.= '_n'
			let sens_list  				 = clk_active_edge.' '.clk_name.' or '. rst_active_edge.' '.rst_name
		elseif a:mode == 'np'
			let clk_active_edge 	 = 'negedge'
			let clk_active_value   = '0'

			let sens_list  				 = clk_active_edge.' '.clk_name.' or '. rst_active_edge.' '.rst_name
		elseif a:mode == 'nn'
			let clk_active_edge 	 = 'negedge'
			let clk_active_value   = '0'
			let rst_active_edge 	 = 'negedge'
			let rst_active_value   = '0'

			let rst_name 					.= '_n'
			let sens_list  				 = clk_active_edge.' '.clk_name.' or '. rst_active_edge.' '.rst_name
		elseif a:mode == 'pp'
			let sens_list  				 = clk_active_edge.' '.clk_name.' or '. rst_active_edge.' '.rst_name
		else
			let sens_list  				 = clk_active_edge.' '.clk_name.' or '. rst_active_edge.' '.rst_name
		endif

		let s:append_line_num = line(".") - 1;

		call s:appendLine(s:indent1."always@(' . sens_list . ')")
    call s:appendLine(s:indent1.'begin')
		if comb_logic == 0 && rst_name != ''
		call s:appendLine(s:indent2.'if('. rst_name .'== '. rst_active_value .')begin')
		call s:appendLine(s:indent3.'')
		call s:appendLine(s:indent2.'end')
		call s:appendLine(s:indent2.'else begin')
		call s:appendLine(s:indent3.'')
		call s:appendLine(s:indent2.'end')
		endif

    call s:appendLine(s:indent1.'end')
    "call cursor(lnum+3,13)
endfunction "}}}1

function s:AlBpp() 
	call s:AddAlwaysBlk('pp', s:atv_snippet_clk, s:atv_snippet_rst)
endfunction 

function s:AlBpn()
	call s:AddAlwaysBlk('pn', s:atv_snippet_clk, s:atv_snippet_rst)
endfunction 

function s:AlB()
	call s:AddAlwaysBlk('comb', s:atv_snippet_clk, s:atv_snippet_rst)
endfunction 

function s:AlBnn() "{{{1
	call s:AddAlwaysBlk('nn', s:atv_snippet_clk, s:atv_snippet_rst)
endfunction "}}}1

function s:AlBp() 
	call s:AddAlwaysBlk('p', s:atv_snippet_clk, s:atv_snippet_rst)
endfunction 

function s:AlBn() "{{{1
	call s:AddAlwaysBlk('n', s:atv_snippet_clk, s:atv_snippet_rst)
endfunction "}}}1

"Comment

function s:AutoComment() "{{{1
    let lnum = line(".")
    let line = getline(lnum)
    if line =~ '^\/\/ by .* \d\d\d\d-\d\d-\d\d'
        let tmp_line = substitute(line,'^\/\/ by .* \d\d\d\d-\d\d-\d\d | ','','')
    else
        let tmp_line = '// by ' . s:AUTHOR. ' ' . strftime("%Y-%m-%d") . ' | ' . line
    endif
    call setline(lnum,tmp_line)
endfunction "}}}1

function s:AutoComment2() "{{{1
    let col = col(".")
    let lnum = line(".")
    if line("'<") == lnum || line("'>") == lnum
        if getline(line("'<")) =~ '^/\*'
            '<
            execute "normal dd"
            '>
            execute "normal dd"
            if lnum != line("'<")
                let lnum = line("'>")-1
            endif
        else
            call append(line("'<")-1,'/*----------------  by '.g:AUTHOR.' '.strftime("%Y-%m-%d").'  ---------------------')
            call append(line("'>")  ,'------------------  by '.g:AUTHOR.' '.strftime("%Y-%m-%d").'  -------------------*/')
            let lnum = line(".")
        endif
    endif
    call cursor(lnum,col)
endfunction "}}}1

function s:AddCurLineComment() "{{{1
    let lnum = line(".")
    let line = getline(lnum)
    let tmp_line = line . ' // ' . g:AUTHOR . ' ' . strftime("%Y-%m-%d") . ' |'
    call setline(lnum,tmp_line)
    normal $
endfunction "}}}1

" vim: tabstop=2 shiftwidth=2 foldmethod=marker
